package controllers

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/neo4j/neo4j-go-driver/v4/neo4j"
)

func (c *PicsController) Post() { //	/posts
	f, h, _ := c.GetFile("file") //获取上传的文件
	ext := path.Ext(h.Filename)
	app := c.GetString("app")
	ver := c.GetString("ver")
	innovation := c.GetString("innovation")
	// function1 := c.GetString("func1")
	// function2 := c.GetString("func2")
	// check := strings.Split(c.GetString("check"), ",") //专项标签
	// assess0 := c.GetString("assess0")
	// assess1 := c.GetString("assess1")
	// assess2 := c.GetString("assess2")
	// assess3 := c.GetString("assess3")
	// assess4 := c.GetString("assess4")
	page := c.GetString("page")
	index := c.GetString("index")
	column := c.GetString("column")
	t := c.GetString("type")
	user := c.GetString("user")
	task := c.GetString("task")

	//创建目录
	uploadDir := "static/upload/" //+ time.Now().Format("2006/01/02/")
	err := os.MkdirAll(uploadDir, 0777)
	if err != nil {
		c.Ctx.WriteString(fmt.Sprintf("%v", err))
		return
	}
	//构造文件名称
	rand.Seed(time.Now().UnixNano())
	randNum := fmt.Sprintf("%d", rand.Intn(9999)+1000)
	hashName := md5.Sum([]byte(time.Now().Format("2006_01_02_15_04_05_") + randNum))
	ext = strings.ToLower(ext)
	fileName := fmt.Sprintf("%x", hashName) + ext
	fpath := uploadDir + fileName
	defer f.Close() //关闭上传的文件，不然的话会出现临时文件不能清除的情况
	err = c.SaveToFile("file", fpath)
	if err != nil {
		c.Ctx.WriteString(fmt.Sprintf("%v", err))
	}

	driver, session := GetNeo4jConn()
	defer driver.Close()
	defer session.Close()

	_, _ = session.WriteTransaction(func(tx neo4j.Transaction) (interface{}, error) {
		//创建图片
		_, err := tx.Run("create (n:Pic{path:$p, innovation:$i})",
			map[string]interface{}{"p": fileName, "i": innovation})
		if err != nil {
			panic(err)
		}

		r, err := tx.Run("match (a:App{name:$n}) return a.name", map[string]interface{}{"n": app})
		if err != nil {
			panic(err)
		}
		if !r.Next() { //没有app 新建app
			_, err = tx.Run("create (f:App{name:$n})", map[string]interface{}{"n": app})
			if err != nil {
				panic(err)
			}
		}

		//创建app和图片的关系
		_, err = tx.Run(`match (a:App{name:$n}),(p:Pic{path:$path})
				create (a)-[v:Vernum{num:$num}]->(p)`,
			map[string]interface{}{"n": app, "num": ver, "path": fileName})
		if err != nil {
			panic(err)
		}

		//循环创建图片和专项标签的关系
		// for _, s := range check {
		// 	_, err = tx.Run(`match (s:Special{name:$s}),(p:Pic{path:$path}) create (p)-[i:Implements]->(s)`,
		// 		map[string]interface{}{"s": s, "path": fileName})
		// 	if err != nil {
		// 		panic(err)
		// 	}
		// }

		//创建图片和测评指标的关系
		// if assess0 != "null" && assess1 != "null" && assess2 != "null" && assess3 != "null" && assess4 != "null" {
		// 	_, err = tx.Run(`match (p:Pic{path: $path}),(a:Assess{class: $class, item: $item, subitem: $subitem, target:$target, example:$example}) create (p)-[i:Implements]->(a)`,
		// 		map[string]interface{}{"path": fileName, "class": assess0, "item": assess1, "subitem": assess2, "target": assess3, "example": assess4})
		// 	if err != nil {
		// 		panic(err)
		// 	}
		// }

		pint, _ := strconv.Atoi(page)
		rank, _ := strconv.Atoi(index)
		if t == "2" { //特色化案例库页面上传 添加图片 页面 图片order的关系

			// if _, ok := UserToTime[user]; !ok { //map里没有用户对应的键
			// 	UserToTime[user] = IndexAndTime{rank, time.Now()}
			// } else if time.Since(UserToTime[user].upTime).Milliseconds() < 2000 { //距上一次时间少于两秒，说明是两秒之内同时上传多张
			// 	rank = UserToTime[user].index + 1
			// 	t := time.Now()
			// 	UserToTime[user] = IndexAndTime{rank, t}
			// } else {
			// 	UserToTime[user] = IndexAndTime{rank, time.Now()}
			// }

			/*
				找到一个最小的位置
				如果小于等于5
				在此位置保存图片
			*/

			ranks, err := tx.Run(`match (u:User{username:$user})-[r:Control]->(t:Task{name:$name}),
				(t)-[i:Include]->(p:Page{order:$order}),(p)-[c:Contains]->(pic:Pic) return c.order as rank order by rank`,
				map[string]interface{}{"user": user, "name": task, "order": pint})
			if err != nil {
				panic(err)
			}

			var page_order int64 = 1
			for ranks.Next() {
				record := ranks.Record()
				rank, _ := record.Get("rank")
				rank_int := rank.(int64)
				if rank_int != page_order {
					break
				} else {
					page_order++
				}
			}
			fmt.Println(page_order)

			if page_order <= 5 {
				_, err = tx.Run(`match (u:User{username:$user})-[r:Control]->(t:Task{name:$name}),
				(t)-[i:Include]->(p:Page{order:$order}),(pic:Pic{path:$path})
				create (p)-[c:Contains{order:$rank}]->(pic)`,
					map[string]interface{}{"user": user, "name": task, "order": pint, "path": fileName, "rank": page_order})
				if err != nil {
					panic(err)
				}
			}
		}

		if t == "3" {
			column, _ := strconv.Atoi(column)
			fmt.Println(rank, column)
			_, err = tx.Run(`match (u:User{username:$user})-[r:Control]->(t:Task{name:$name}),
				(t)-[i:Include]->(p:Page{order:$order}),(p)-[:Inc]->(col:Col{col:$column}),(pic:Pic{path:$path})
				create (col)-[c:Contains{order:$rank}]->(pic)`,
				map[string]interface{}{"user": user, "name": task, "order": pint, "path": fileName, "rank": rank + 1, "column": column})
			if err != nil {
				panic(err)
			}
		}
		return nil, nil
	})

	c.Ctx.WriteString("")
}

func (c *PicsController) Get() {
	uri := c.Ctx.Request.RequestURI
	b, err := ioutil.ReadFile("./static/upload" + uri) // just pass the file name
	if err != nil {
		fmt.Print(err)
	}
	c.Ctx.WriteString(string(b))
}

func (c *PicsController) Getbv() {
	driver, session := GetNeo4jConn()
	defer driver.Close()
	defer session.Close()
	results, _ := session.ReadTransaction(func(tx neo4j.Transaction) (interface{}, error) {
		result, _ := tx.Run("match (a:App) return a.name as name", map[string]interface{}{})
		var banks []Bank
		for result.Next() {
			record := result.Record()
			var bank Bank
			appname, _ := record.Get("name")
			name, _ := appname.(string)
			bank.BankName = name
			bank.Value = name
			r, _ := tx.Run("match (a:App{name:$n})-[v:Vernum]->(pic:Pic) return distinct v.num as num order by num desc",
				map[string]interface{}{"n": name})
			for r.Next() {
				rec := r.Record()
				var ver Ver
				num, _ := rec.Get("num")
				numstr, _ := num.(string)
				ver.Vername = numstr
				ver.Value = numstr
				bank.Vers = append(bank.Vers, ver)
			}
			banks = append(banks, bank)
		}
		return banks, nil
	})
	c.Data["json"] = results
	c.ServeJSON()
}

func (c *PicsController) FilterImgs() {
	var cons []Condition
	json.Unmarshal(c.Ctx.Input.RequestBody, &cons)
	page := c.GetString("p")
	page_int, _ := strconv.Atoi(page)

	driver, session := GetNeo4jConn()
	defer driver.Close()
	defer session.Close()

	m := map[string]interface{}{}
	assess := map[string]string{}
	match_clause := []string{"match (p:Pic)"}
	var where_clause []string
	for i, con := range cons {
		t, name := con.T, con.Name
		switch t {
		case "func":
			funcs := strings.Split(name, "|")
			where_clause = append(where_clause, "p.f1=$f1 and p.f2=$f2")
			m["f1"], m["f2"] = funcs[0], funcs[1]
		case "app":
			apps := strings.Split(name, "|")
			match_clause = append(match_clause, "(p)<-[v:Vernum{num:$ver}]-(app:App{name:$app})")
			m["app"], m["ver"] = apps[0], apps[1]
		case "tag":
			match_clause = append(match_clause, "(p)-[:Implements]->(:Special{name:$special"+strconv.Itoa(i)+"})")
			m["special"+strconv.Itoa(i)] = name
		case "special":
			where_clause = append(where_clause, "p.innovation=$innovation")
			m["innovation"] = name
		case "class":
			assess["class"], m["class"] = "$class", name
		case "item":
			assess["item"], m["item"] = "$item", name
		case "subitem":
			assess["subitem"], m["subitem"] = "$subitem", name
		case "target":
			assess["target"], m["target"] = "$target", name
		case "example":
			assess["example"], m["example"] = "$example", name
		}
	}

	if _, ok := assess["class"]; ok {
		assessstr, _ := json.Marshal(assess)
		attr := strings.Join(strings.Split(string(assessstr), "\""), "")
		match_clause = append(match_clause, "(p)-[:Implements]->(a:Assess"+attr+")")
	}

	match := strings.Join(match_clause, ", ")
	where := strings.Join(where_clause, " and ")
	if where != "" {
		where = "where " + where
	}
	cypher := strings.Join([]string{match, where}, " ")
	m["head"] = (page_int - 1) * 20

	var pics []Pic
	var total_pic TotalPic
	results, _ := session.ReadTransaction(func(tx neo4j.Transaction) (interface{}, error) {
		result, err := tx.Run(cypher+" return p.path as path, p.f1 as f1,p.f2 as f2,p.innovation as innovation skip $head limit 20", m)
		if err != nil {
			panic(err)
		}
		for result.Next() { //对每一个图片
			var pic Pic
			record := result.Record()
			path, f1, f2, innovation := record.GetByIndex(0), record.GetByIndex(1), record.GetByIndex(2), record.GetByIndex(3)
			pic.Path, _ = path.(string)
			pic.F1, _ = f1.(string)
			pic.F2, _ = f2.(string)
			pic.Innovation, _ = innovation.(string)

			r, e := tx.Run(`match (p:Pic{path:$path})-[:Implements]->(a:Assess) 
			return a.class as class,a.item as item,a.subitem as subitem,a.target as target, a.example as example`, map[string]interface{}{"path": pic.Path})
			if e != nil {
				panic(e)
			}

			if r.Next() {
				record := r.Record()
				class := record.GetByIndex(0)
				item := record.GetByIndex(1)
				subitem := record.GetByIndex(2)
				target := record.GetByIndex(3)
				example := record.GetByIndex(4)
				pic.Assess0, _ = class.(string)
				pic.Assess1, _ = item.(string)
				pic.Assess2, _ = subitem.(string)
				pic.Assess3, _ = target.(string)
				pic.Assess4, _ = example.(string)

			}

			//搜索关联的专项指标
			r, e = tx.Run(`match (p:Pic{path:$path})-[:Implements]->(s:Special) return s.name as name`, map[string]interface{}{"path": pic.Path})
			if e != nil {
				panic(e)
			}

			for r.Next() {
				record := r.Record()
				name := record.GetByIndex(0)
				namestr := name.(string)
				pic.Special = append(pic.Special, namestr)
			}

			pics = append(pics, pic)
		}

		result, err = tx.Run(cypher+" return count(p) as count", m)
		if err != nil {
			panic(err)
		}
		if result.Next() {
			record := result.Record()
			count := record.GetByIndex(0)
			c, _ := count.(int64)
			total_pic.Imgs, total_pic.Total = pics, c
		}
		return total_pic, nil
	})
	c.Data["json"] = results
	c.ServeJSON()
}

func (c *PicsController) Dropimg() {
	path := c.GetString("path")
	driver, session := GetNeo4jConn()
	defer driver.Close()
	defer session.Close()
	result, err := session.WriteTransaction(func(tx neo4j.Transaction) (interface{}, error) {
		var result int64
		r, e := tx.Run(`match (p:Page)-[:Contains]->(pic:Pic{path:$path}) return count(p)`, map[string]interface{}{"path": path})
		if r.Next() {
			record := r.Record()
			total := record.GetByIndex(0)
			result, _ = total.(int64)
		}
		if e != nil {
			panic(e)
		}
		_, e = tx.Run(`match(p:Pic{path:$path}) detach delete p;`, map[string]interface{}{"path": path})
		return result, e
	})
	if err != nil {
		panic(err)
	}
	os.Remove("static/upload/" + path)
	msg := fmt.Sprintf("删除成功，并同时从%d个页面上删除", result)
	c.Ctx.WriteString(msg)
}
